﻿' ----------------------------------------------------------------------------------
' Microsoft Developer & Platform Evangelism
' 
' Copyright (c) Microsoft Corporation. All rights reserved.
' 
' THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
' EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES 
' OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ----------------------------------------------------------------------------------
' The example companies, organizations, products, domain names,
' e-mail addresses, logos, people, places, and events depicted
' herein are fictitious.  No association with any real company,
' organization, product, domain name, email address, logo, person,
' places, or events is intended or should be inferred.
' ----------------------------------------------------------------------------------

Imports Microsoft.Samples.WindowsPhoneCloud.Web.Infrastructure
Imports System.Xml
Imports System.Text
Imports System.Globalization

Namespace Serializers

    Public Class JsonSerializer
        Implements IFormatSerializer
        Public Function SerializeReply(ByVal originalReply As Object, <System.Runtime.InteropServices.Out()> ByRef contentType As String) As String Implements IFormatSerializer.SerializeReply
            If HttpContext.Current Is Nothing Then
                Throw New HttpException("Service Hosting ASP.NET Compatibility Mode must be enabled for JSON serialization.")
            End If

            contentType = HttpConstants.MimeApplicationJson
            Return ConvertXmlToJson(TryCast(originalReply, XmlDocument))
        End Function

        Private Shared Function ConvertXmlToJson(ByVal xmlDoc As XmlDocument) As String
            Dim builder = New StringBuilder()
            builder.Append("{ ")
            ConvertXmlToJsonNode(builder, xmlDoc.DocumentElement, True)
            builder.Append("}")

            Return builder.ToString()
        End Function

        Private Shared Sub ConvertXmlToJsonNode(ByVal jsonBuilder As StringBuilder, ByVal node As XmlElement, ByVal showNodeName As Boolean)
            Dim childAdded = False
            If showNodeName Then
                jsonBuilder.Append("""" & SafeJsonString(node.Name) & """: ")
            End If

            jsonBuilder.Append("{")
            Dim childNodeNames = New SortedList()

            If node.Attributes IsNot Nothing Then
                For Each attr As XmlAttribute In node.Attributes
                    StoreChildNode(childNodeNames, attr.Name, attr.InnerText)
                Next attr
            End If

            For Each childNode As XmlNode In node.ChildNodes
                childAdded = True

                If TypeOf childNode Is XmlText Then
                    StoreChildNode(childNodeNames, "value", childNode.InnerText)
                ElseIf TypeOf childNode Is XmlElement Then
                    StoreChildNode(childNodeNames, childNode.Name, childNode)
                End If
            Next childNode

            For Each childName As String In childNodeNames.Keys
                childAdded = True

                Dim childNodeNamesList = CType(childNodeNames(childName), ArrayList)
                If childNodeNamesList.Count = 1 AndAlso (TypeOf childNodeNamesList(0) Is String) Then
                    OutputNode(childName, childNodeNamesList(0), jsonBuilder, True)
                Else
                    jsonBuilder.Append(" """ & SafeJsonString(childName) & """: [ ")
                    For Each child As Object In childNodeNamesList
                        OutputNode(childName, child, jsonBuilder, False)
                    Next child

                    jsonBuilder.Remove(jsonBuilder.Length - 2, 2)
                    jsonBuilder.Append(" ], ")
                End If
            Next childName

            jsonBuilder.Remove(jsonBuilder.Length - 2, 2)
            jsonBuilder.Append(" }")

            If childAdded Then
                jsonBuilder.Append(" }")
            Else
                jsonBuilder.Append(" null")
            End If
        End Sub

        Private Shared Sub StoreChildNode(ByVal childNodeNames As SortedList, ByVal nodeName As String, ByVal nodeValue As Object)
            If TypeOf nodeValue Is XmlElement Then
                Dim childNode = CType(nodeValue, XmlNode)
                If childNode.Attributes.Count = 0 Then
                    Dim children = childNode.ChildNodes
                    If children.Count = 0 Then
                        nodeValue = Nothing
                    ElseIf children.Count = 1 AndAlso (TypeOf children(0) Is XmlText) Then
                        nodeValue = (CType(children(0), XmlText)).InnerText
                    End If
                End If
            End If

            Dim tempNodeValueList = childNodeNames(nodeName)
            Dim nodeValueList As ArrayList
            If tempNodeValueList Is Nothing Then
                nodeValueList = New ArrayList()
                childNodeNames(nodeName) = nodeValueList
            Else
                nodeValueList = CType(tempNodeValueList, ArrayList)
            End If

            nodeValueList.Add(nodeValue)
        End Sub

        Private Shared Sub OutputNode(ByVal childname As String, ByVal childNodeNamesList As Object, ByVal jsonBuilder As StringBuilder, ByVal showNodeName As Boolean)
            If childNodeNamesList Is Nothing Then
                If showNodeName Then
                    jsonBuilder.Append("""" & SafeJsonString(childname) & """: ")
                End If

                jsonBuilder.Append("null")
            Else
                Dim childNodeNamesListStr = TryCast(childNodeNamesList, String)
                If String.IsNullOrWhiteSpace(childNodeNamesListStr) Then
                    If showNodeName Then
                        jsonBuilder.Append("""" & SafeJsonString(childname) & """: ")
                    End If

                    childNodeNamesListStr = childNodeNamesListStr.Trim()
                    jsonBuilder.Append("""" & SafeJsonString(childNodeNamesListStr) & """")
                Else
                    ConvertXmlToJsonNode(jsonBuilder, CType(childNodeNamesList, XmlElement), showNodeName)
                End If
            End If

            jsonBuilder.Append(", ")
        End Sub

        Private Shared Function SafeJsonString(ByVal value As String) As String
            Dim safeValue = New StringBuilder(value.Length)
            For Each ch As Char In value
                If Char.IsControl(ch) OrElse ch = "'"c Then
                    Dim ich = AscW(ch)
                    safeValue.Append("\u" & ich.ToString("x4", CultureInfo.InvariantCulture))
                    Continue For
                ElseIf ch = """"c OrElse ch = "\"c OrElse ch = "/"c Then
                    safeValue.Append("\"c)
                End If

                safeValue.Append(ch)
            Next ch

            Return safeValue.ToString()
        End Function
    End Class
End Namespace